package com.example.prom.aspect;

import com.alibaba.fastjson.JSON;
import com.example.prom.annotation.CounterMetricAnnotation;
import com.example.prom.vo.OrderVO;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import org.apache.logging.log4j.util.Strings;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

@Component
@Aspect
public class CounterMetricAspect {
    protected MeterRegistry meterRegistry;

    private volatile Map<String, Counter> metricsMap = new HashMap<String, Counter>();

    public CounterMetricAspect(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }

    @PostConstruct
    public void after() {
        this.meterRegistry.config().commonTags("application", "prom-grafana-metric");
    }

    @Pointcut("@annotation(com.example.prom.annotation.CounterMetricAnnotation)")
    public void pointcut() {
    }

    @Around(value = "pointcut()")
    public Object process(ProceedingJoinPoint joinPoint) throws Throwable {
        Method targetMethod = ((MethodSignature) joinPoint.getSignature()).getMethod();
        Method currentMethod = ClassUtils.getUserClass(joinPoint.getTarget().getClass())
                .getMethod(targetMethod.getName(), targetMethod.getParameterTypes());
        if (currentMethod.isAnnotationPresent(CounterMetricAnnotation.class)) {
            CounterMetricAnnotation methodMetric = currentMethod.getAnnotation(CounterMetricAnnotation.class);
            processCountMetric(joinPoint, currentMethod, methodMetric,"total", "-", "-");
        }
        return joinPoint.proceed();
    }

    @AfterReturning(returning="rvt", pointcut = "pointcut()")
    public void doAfterReturning(JoinPoint joinPoint, Object rvt) throws Throwable {
        Method targetMethod = ((MethodSignature) joinPoint.getSignature()).getMethod();
        Method currentMethod = ClassUtils.getUserClass(joinPoint.getTarget().getClass())
                .getMethod(targetMethod.getName(), targetMethod.getParameterTypes());
        Object[] args = joinPoint.getArgs();
        String template = "-";
        String areacode = "-";
//        for (Object arg : args) {
//            if (arg instanceof OrderVO) {
//                OrderVO orderVO = (OrderVO) arg;
//                template = orderVO.getTemplate();
//                areacode = orderVO.getAreacode();
//            }
//        }
        for (Object arg : args) {
                String className = arg.getClass().getName();
                Class<?> aClass = Class.forName(className);
                Field[] declaredFields = aClass.getDeclaredFields();
                for (Field declaredField : declaredFields) {
                    declaredField.setAccessible(true);
                    String fieldName = declaredField.getName();
                    if ("template".equalsIgnoreCase(fieldName)){
                        Object o = declaredField.get(arg);
                        template = (String) o;
                    }
                    if ("areacode".equalsIgnoreCase(fieldName)) {
                        Object o = declaredField.get(arg);
                        areacode = (String) o;
                    }
                }
//                //3.匹配到iAcsClient属性，赋值
//                for(Field field : fieldList){
//                    if(("iAcsClient").equals(field.getName())){
//                        //4.如果此属性为非public属性的情况时，需要设置属性可达，否则会抛出IllegalAccessException异常
//                        field.setAccessible(true);
//                        //5.生成认证对象
//                        //5.1获取指定属性值
//                        Map<String, String> fieldsValue = getFieldsValue(fieldList, o[i]);
//                        //5.2创建 Profile。生成 IClientProfile 的对象 profile，该对象存放 AccessKeyID 和 AccessKeySecret 和默认的地域信息
//                        DefaultProfile profile = DefaultProfile.getProfile(fieldsValue.get(regionId), fieldsValue.get(AccessKey_ID), fieldsValue.get(AccessKey_Secret));
//                        //5.3创建 Client。从 IClientProfile 类中再生成 IAcsClient 的对象 client，后续获得 response 都需要从 IClientProfile 中获得。
//                        IAcsClient client = new DefaultAcsClient(profile);
//                        //6.赋值
//                        field.set(o[i],client);
//                    }
//                }

        }
        if (currentMethod.isAnnotationPresent(CounterMetricAnnotation.class)) {
            CounterMetricAnnotation methodMetric = currentMethod.getAnnotation(CounterMetricAnnotation.class);
            if (rvt instanceof Map) {
                if (methodMetric.name().equals("bafang.common.sms")) {
                    if (((Map) rvt).get("success").equals("true")) {
                        processCountMetric(joinPoint, currentMethod, methodMetric,"success", template, areacode);
                    } else {
                        processCountMetric(joinPoint, currentMethod, methodMetric,"error", template, areacode);
                    }
                }
            } else if (rvt instanceof Boolean) {
                if (methodMetric.name().equals("bafang.common.email") || methodMetric.name().equals("bafang.common.sms")) {
                    if ((Boolean) rvt) {
                        processCountMetric(joinPoint, currentMethod, methodMetric,"success", template, areacode);
                    } else {
                        processCountMetric(joinPoint, currentMethod, methodMetric,"error", template, areacode);
                    }
                }
            }
        }
    }

    private void processCountMetric(JoinPoint joinPoint, Method currentMethod, CounterMetricAnnotation metric,
                                    String description, String template, String areacode) {
        final String name = StringUtils.hasText(metric.name()) ? metric.name() : currentMethod.getName();
        final String desc = StringUtils.hasText(metric.description()) ? metric.description() : name;
        List<String> tagList = new ArrayList<>();
        if (Strings.isNotEmpty(description)) {
            tagList.add("description");
            tagList.add(description);
        }
        if (Strings.isNotEmpty(template)) {
            tagList.add("template");
            tagList.add(template);
        }
        if (Strings.isNotEmpty(areacode)) {
            tagList.add("areacode");
            tagList.add(areacode);
        }
        String[] tags = metric.tags();
        if (tagList.size() > 0) {
            int length = tags.length;
            String[] descTags = Arrays.copyOf(tags, length + tagList.size());
            for (int i = 0; i < tagList.size(); i++) {
                descTags[length + i] = tagList.get(i);
            }
            String counterMetric = org.apache.commons.lang3.StringUtils.join(descTags);
            metricsMap.computeIfAbsent(counterMetric,
                    k -> Counter.builder(name).tags(descTags).description(desc).register(meterRegistry)).increment();

        } else {
            String counterMetric = org.apache.commons.lang3.StringUtils.join(tags);
            metricsMap.computeIfAbsent(counterMetric,
                    k -> Counter.builder(name).tags(tags).description(desc).register(meterRegistry)).increment();
        }
        System.out.println("metricsMap:"+ JSON.toJSONString(metricsMap));
    }

//    private Map<String,String> getFieldsValue(List<Field> fieldList,Object object) throws Exception {
//        Map<String,String> result = new HashMap<>();
//        for(Field field : fieldList){
//            if(("regionId").equals(field.getName())){
//                //如果此属性为非public属性的情况时，需要设置属性可达，否则会抛出IllegalAccessException异常
//                field.setAccessible(true);
//                //获取属性值存入Map
//                result.put(regionId,field.get(object).toString());
//            }
//            if(("authentication").equals(field.getName())){
//                field.setAccessible(true);
//                //获取属性值存入Map
//                Authentication authentication = (Authentication)field.get(object);
//                result.put(AccessKey_ID,authentication.getAccessKey_ID());
//                result.put(AccessKey_Secret,authentication.getAccessKey_Secret());
//            }
//        }
//        return result;
//    }

}
